package com.imis.base.aspect;

import com.imis.base.util.ConvertUtils;
import org.aspectj.lang.JoinPoint;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;

/**
 * <p>
 * BaseAspect<br>
 * Spring Expression Language
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月31日 16:50
 */
public class BaseAspect {

    /**
     * 获取被拦截方法对象
     * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
     * 而缓存的注解在实现类的方法上
     * 所以应该使用反射获取当前对象的方法对象
     *
     * @param joinPoint - JoinPoint
     * @return Method -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/24 14:58
     */
    protected static Method getMethod(JoinPoint joinPoint) {
        // 获取参数的类型
        Object[] args = joinPoint.getArgs();
        Class[] argTypes = new Class[joinPoint.getArgs().length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        Method method = null;
        try {
            method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
        return method;
    }

    /**
     * 获取被拦截方法类名
     *
     * @param joinPoint - JoinPoint
     * @return String -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/24 14:58
     */
    protected static String getClassName(JoinPoint joinPoint) {
        return joinPoint.getTarget().getClass().getName();
    }

    /**
     * 解析SPEL
     *
     * @param spELString -
     * @param joinPoint  -
     * @return String -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/31 16:44
     */
    protected static String generateStringBySpEL(String spELString, JoinPoint joinPoint) {
        // 1.获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        // 通过ProceedingJoinPoint获取被注解方法
        Method method = getMethod(joinPoint);
        String[] paraNameArr = localVariableTableParameterNameDiscoverer.getParameterNames(method);
        // 2.使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        // 3.SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 4.把方法参数放入SPEL上下文中
        // 通过ProceedingJoinPoint获取被注解方法的形参
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        if (ConvertUtils.isNotEmpty(spELString)){
            return parser.parseExpression(spELString).getValue(context, String.class);
        }
        return spELString;
    }

    /**
     * 解析SPEL
     *
     * @param spELString -
     * @param joinPoint  -
     * @return String -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/31 16:44
     */
    protected static String generateStringBySpEL2(String spELString, JoinPoint joinPoint) {
        // 通过ProceedingJoinPoint获取被注解方法
        Method method = getMethod(joinPoint);
        // 使用Spring的 DefaultParameterNameDiscoverer 获取方法形参名数组
        DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(method);
        // 解析过后的Spring表达式对象
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        Expression expression = spelExpressionParser.parseExpression(spELString);
        // Spring的表达式上下文对象
        EvaluationContext context = new StandardEvaluationContext();
        // 通过ProceedingJoinPoint获取被注解方法的形参
        Object[] args = joinPoint.getArgs();
        // 给上下文赋值
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        // 表达式从上下文中计算出实际参数值
        /**
         * 例如：@annotation(key="#student.name")
         *  method(Student student)
         * 可以解析出方法形参的某属性值，return “xiaoming”;
         */
        return expression.getValue(context, String.class);
    }

}
